#include "TextureMgr.h"
#include <FreeImage.h>
#include <iostream>

std::string TextureMgr::diffuseTexture = "texture.jpg";
std::string TextureMgr::dirtTexture = "dirtTexture.jpg";
TextureMgr* TextureMgr::instance = NULL;

float TextureMgr::cabinDirtTexture[];
float TextureMgr::cabinDiffuseTexture[] = {
		236, 729,
		0, 729,
		0, 426,
		236, 426,

		0, 425,
		0, 0,
		601, 0,
		601, 425,

		602, 425,
		602, 0,
		1023, 0,
		1023, 425,

		0, 425,
		601, 425,
		601, 0,
		0, 0,

		237, 426,
		237, 729,
		900, 729,
		900, 426,

		532, 426,
		1023, 426,
		1023, 729,
		532, 729
};

float TextureMgr::towerColumnDirtTexture[];
float TextureMgr::towerColumnDiffuseTexture[] = {
		0, 743,
		1023, 743,
		1023, 813,
		0, 813,

		0, 813,
		1023, 813,
		1023, 883,
		0, 883,

		0, 883,
		1023, 883,
		1023, 953,
		0, 953,

		0, 953,
		0, 1023,
		1023, 1023,
		1023, 953,

		0, 1023,
		0, 1023,
		0, 1023,
		0, 1023,

		0, 1023,
		0, 1023,
		0, 1023,
		0, 1023
};

float TextureMgr::towerTopDirtTexture[];
float TextureMgr::towerTopDiffuseTexture[] = {
		0, 1000,
		0, 970,
		500, 970,
		500, 1000,

		0, 1000,
		0, 970,
		500, 970,
		500, 1000,

		0, 1000,
		0, 970,
		500, 970,
		500, 1000,

		0, 1000,
		0, 970,
		500, 970,
		500, 1000,

		0, 1023,
		0, 732,
		291, 732,
		291, 1023,

		0, 1023,
		0, 732,
		291, 732,
		291, 1023
};

float TextureMgr::towerLatticeDirtTexture[];
float TextureMgr::towerLatticeDiffuseTexture[] {
		0, 1023,
		0, 732,
		30, 732,
		30, 1023,

		30, 1023,
		30, 732,
		60, 732,
		60, 1023,

		60, 1023,
		60, 732,
		90, 732,
		90, 1023,

		90, 1023,
		120, 1023,
		120, 732,
		90, 732,

		90, 1023,
		120, 1023,
		120, 732,
		90, 732,

		30, 1023,
		30, 732,
		60, 732,
		60, 1023
};

float TextureMgr::boomColumnDirtTexture[];
float TextureMgr::boomColumnDiffuseTexture[] = {
		0, 813,
		0, 743,
		729, 743,
		729, 813,

		0, 813,
		729, 813,
		729, 883,
		0, 883,

		729, 883,
		729, 953,
		0, 953,
		0, 883,

		0, 953,
		0, 1023,
		729, 1023,
		729, 953,

		729, 813,
		0, 813,
		0, 883,
		729, 883,

		729, 1023,
		729, 953,
		0, 953,
		0, 1023
};

float TextureMgr::cableDirtTexture[];
float TextureMgr::cableDiffuseTexture[] {
		0, 1023,
		100, 1023,
		0, 1015,

		0, 1015,
		100, 1023,
		100, 1015
};


TextureMgr::TextureMgr() {
	for (int i = 0; i < 48; i++) {
		cabinDirtTexture[i] = cabinDiffuseTexture[i] / 511;
		cabinDiffuseTexture[i] = cabinDiffuseTexture[i] / 1023;

		towerColumnDirtTexture[i] = towerColumnDiffuseTexture[i] / 511;
		towerColumnDiffuseTexture[i] = towerColumnDiffuseTexture[i] / 1023;

		towerTopDirtTexture[i] = towerTopDiffuseTexture[i] / 511;
		towerTopDiffuseTexture[i] = towerTopDiffuseTexture[i] / 1023;

		towerLatticeDirtTexture[i] = towerLatticeDiffuseTexture[i] / 511;
		towerLatticeDiffuseTexture[i] = towerLatticeDiffuseTexture[i] / 1023;

		boomColumnDirtTexture[i] = boomColumnDiffuseTexture[i] / 511;
		boomColumnDiffuseTexture[i] = boomColumnDiffuseTexture[i] / 1023;
	}

	for (int i = 0; i < 12; i++) {
		cableDirtTexture[i] = cableDiffuseTexture[i] / 511;
		cableDiffuseTexture[i] = cableDiffuseTexture[i] / 1023;
	}

	loadedTexture = -1;
//	textureIds.insert(std::pair<std::string, int>(diffuseTexture, 0));

	TextureInfo* tex1 = new TextureInfo;
	tex1->filename = diffuseTexture;
	tex1->textureUnit = GL_TEXTURE0;
	tex1->samplerName = "DiffuseTex";
	tex1->samplerId = 0;
	tex1->format = GL_BGR;
	tex1->loaded = false;

	TextureInfo* tex2 = new TextureInfo;
	tex2->filename = dirtTexture;
	tex2->textureUnit = GL_TEXTURE1;
	tex2->samplerName = "DirtTex";
	tex2->samplerId = 1;
	tex2->format = GL_BGR;
	tex2->loaded = false;

	texturesInfo.insert(std::pair<std::string, TextureInfo*>(tex1->filename, tex1));
	texturesInfo.insert(std::pair<std::string, TextureInfo*>(tex2->filename, tex2));
}

TextureMgr* TextureMgr::getInstance() {
	if (instance == NULL) {
		instance = new TextureMgr();
	}
	return instance;
}

void TextureMgr::loadTextures(GLuint programHandler) {
	std::map<std::string, TextureInfo*>::iterator it = texturesInfo.begin();
	for (; it != texturesInfo.end(); it++) {
		TextureInfo* info = it->second;
		initTexture(info);

		// Set the Tex1 sampler uniform to refer to texture unit 0
		int loc = glGetUniformLocation(programHandler, info->samplerName.c_str());
		if( loc >= 0 )
		{
			glUniform1i(loc, info->samplerId);
		}
		else
		{
			std::cerr << "Uniform variable " << info->samplerName << " not found!" << std::endl;
		}
	}
}

void TextureMgr::initTexture(TextureInfo* info) {
	if (info->loaded) {
		glActiveTexture(info->textureUnit);
		glBindTexture(GL_TEXTURE_2D, info->textureId);
		return;
	}
//	if (textureIds.at(filename) == loadedTexture) {
//		return;
//	}
	//image format
	FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
	//pointer to the image, once loaded
	FIBITMAP *dib(0);
	//pointer to the image data
	BYTE* bits(0);
	//image width and height
	unsigned int width(0), height(0);

	//check the file signature and deduce its format
	fif = FreeImage_GetFileType(info->filename.c_str(), 0);
	//if still unknown, try to guess the file format from the file extension
	if(fif == FIF_UNKNOWN)
		fif = FreeImage_GetFIFFromFilename(info->filename.c_str());
	//if still unkown, return failure
	if(fif == FIF_UNKNOWN)
		return;

	//check that the plugin has reading capabilities and load the file
	if(FreeImage_FIFSupportsReading(fif))
		dib = FreeImage_Load(fif, info->filename.c_str());
	//if the image failed to load, return failure
	if(!dib)
		return;

	//is the file of the correct type
//	FREE_IMAGE_COLOR_TYPE type = FreeImage_GetColorType(dib);
//	if(FIC_RGBALPHA != type)
//	{
//		//convert to type
//		FIBITMAP* ndib = FreeImage_ConvertTo32Bits(dib);
//		dib = ndib;
//	}

	//retrieve the image data
	bits = FreeImage_GetBits(dib);
	//get the image width and height
	width = FreeImage_GetWidth(dib);
	height = FreeImage_GetHeight(dib);
	//if this somehow one of these failed (they shouldn't), return failure
	if((bits == 0) || (width == 0) || (height == 0))
		return;

	// Copy file to OpenGL
	glActiveTexture(info->textureUnit);
	GLuint textureId;
	glGenTextures(1, &textureId);
	glBindTexture(GL_TEXTURE_2D, textureId);
	info->textureId = textureId;

	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, info->format, GL_UNSIGNED_BYTE, bits);
//    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bits);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

//	// Set the UseTexture uniform to use texture
//	int useTextureLocation = glGetUniformLocation(programHandler, "UseTexture");
//
//	if( useTextureLocation >= 0 )
//	{
//		glUniform1f(useTextureLocation, 1.0f);
//	}
//	else
//	{
//		std::cerr << "Uniform variable UseTexture not found!" << std::endl;
//	}

	//Free FreeImage's copy of the data
	FreeImage_Unload(dib);

	info->loaded = true;

//	loadedTexture = textureIds.at(filename);
}
